﻿using MoMoTransferVN_WP.utils;
using Org.BouncyCastle.Crypto.Tls;
using SocketEx;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Storage.Streams;

namespace MoMoTransferVN_WP.momo.transporter
{
    public class MoMoTcpClient
    {
        public const int STATE_INIT = 0;
        public const int STATE_CONNECTING = 1;
        public const int STATE_CONNECTED = 2;
        public const int STATE_STOPPED = 3;
        public const int STATE_DISCONNECTED = 4;

        public interface IConnectStatus
        {

            void onConnected();

            void onDisconnectWithError(String error);

            void onMessageCome(MomoMessage msg);

        }

        // The maximum size of the data buffer to use with the asynchronous socket methods
        const int MAX_BUFFER_SIZE = 1024;

        private int mState = 0;

        private IConnectStatus ckb;

        private volatile bool mIsRunning = false;

        private volatile SecureTcpClient mConnection;

        private Thread mThread;

        public MoMoTcpClient(IConnectStatus ckb)
        {
            this.ckb = ckb;

            //set init state
            mState = STATE_INIT;

            LogUtils.f("start thread");

            //start a thread
            mIsRunning = true;
            mThread = new Thread(handleMessage);
            mThread.IsBackground = true;
            mThread.Start();
        }

        public void forceStop(){
            mIsRunning = false;
        }

        public void sendMoMoMesage(MomoMessage msg)
        {
            if (mState == STATE_CONNECTED && mIsRunning)
            {

                LogUtils.f(String.Format("onMessageSend: --type: {0} // --cmdIndex: {1} // --cmdPhone: {2}", msg.cmdType, msg.cmdIndex, msg.cmdPhone));

                int len = ((msg.cmdBody != null) ? msg.cmdBody.Length : 0) + MomoMessage.MIN_LEN;

                if (len >= MomoMessage.MIN_LEN && len < MAX_BUFFER_SIZE)
                {
                    MemoryStream stream = new MemoryStream();
                    using (BinaryWriter temp = new BinaryWriter(stream))
                    {
                        temp.Write(MomoMessage.STX_BYTE);
                        temp.Write(IPAddress.HostToNetworkOrder(len));
                        temp.Write(IPAddress.HostToNetworkOrder(msg.cmdType));
                        temp.Write(IPAddress.HostToNetworkOrder(msg.cmdIndex));
                        temp.Write(IPAddress.HostToNetworkOrder(msg.cmdPhone));
                        if (msg.cmdBody != null)
                        {
                            temp.Write(msg.cmdBody);
                        }
                        temp.Write(MomoMessage.ETX_BYTE);
                    }
                    byte[] bytes = stream.ToArray();
                    stream.Dispose();

                    //Send
                    if (mConnection != null)
                    {
                        try
                        {
                            BinaryWriter writer = new BinaryWriter(mConnection.GetStream());
                            writer.Write(bytes);
                            writer.Flush();
                        }
                        catch (ArgumentException e)
                        {
                            LogUtils.f("send not success with errors: " + e.Message);
                        }
                    }
                    else
                    {
                        LogUtils.f("mConnect is null");
                    }
                }
                else
                {
                    //we dont allow msg length over BUFLEN
                    Debug.WriteLine("over BUFLEN");
                }
            }
            else
            {
                LogUtils.f("can't send msg to server. please connect before send it.");
            }
        }

        private void handleMessage()
        {

            try
            {
                mState = STATE_CONNECTING;
                mConnection = new SecureTcpClient(MoMoAssets.SERVER_IP, MoMoAssets.SERVER_PORT);
            }
            catch (System.IO.IOException ex)
            {
                ckb.onDisconnectWithError("Không có kết nối đến máy chủ");
                LogUtils.f("Not connect to server, reconnecting to server. " + ex.Message);
                mIsRunning = false;
                handleMessage();
                return;
            }

            try
            {
                mIsRunning = true;
                ckb.onConnected();
                mState = STATE_CONNECTED;
                sendMoMoMesage(MoMoReq.processHello());

                // Create a thread to read data sent from the server.
                ThreadPool.QueueUserWorkItem(
                   delegate
                   {
                       while (true)
                       {
                           //LogUtils.f("running...");
                           try
                           {
                                   //Receive
                                   BinaryReader reader = new BinaryReader(mConnection.GetStream());
                                   byte first = reader.ReadByte();
                                   int msgLen = IPAddress.HostToNetworkOrder(reader.ReadInt32());
                                   int cmdType = IPAddress.HostToNetworkOrder(reader.ReadInt32());
                                   long cmdIndex = IPAddress.HostToNetworkOrder(reader.ReadInt64());
                                   int cmdPhone = IPAddress.HostToNetworkOrder(reader.ReadInt32());
                                   byte[] cmdBody = new byte[msgLen - MomoMessage.MIN_LEN];

                                   //copy body data to buffer
                                   reader.Read(cmdBody, 0, cmdBody.Length);

                                   //ext byte
                                   byte last = reader.ReadByte();

                                   if (first != MomoMessage.STX_BYTE || last != MomoMessage.ETX_BYTE)
                                   {
                                       // network error here;
                                       LogUtils.f("checksum parse error ");
                                   }
                                   else
                                   {
                                       //create momo msg
                                       MomoMessage msg = new MomoMessage(cmdType, cmdIndex, cmdPhone, cmdBody);
                                       //process msg
                                       ckb.onMessageCome(msg);
                                   }
                           }
                           catch (EndOfStreamException e)
                           {
                               LogUtils.f("aaaaaa " + e.Message);
                           }
                           catch (ObjectDisposedException ex)
                           {
                               LogUtils.f("bbbbbb " + ex.Message);
                           }
                           catch (IOException exx)
                           {
                               LogUtils.f("cccccc " + exx.Message);
                           }
                           catch (Exception exxx)
                           {
                               LogUtils.f("dddddd " + exxx.Message);
                           }
                       }
                   });
                LogUtils.f("thread has stopped, need start again...");
            }
            catch (Exception e)
            {
                LogUtils.f("Error: " + e.Message);
                ckb.onDisconnectWithError(e.Message);
            }
        }


    }
}

